//ys_drv_gfx_int035.c
//drv_gfx_generic.c
//INT035 Display Driver
//                      2017.8.15   by  YSDK Design
///////////////////////////////////////////////////////////////////

#include "framework/gfx/driver/controller/generic/drv_gfx_generic.h"
//#include "framework/gfx/driver/controller/int035/ys_drv_gfx_int035.h"

#include "gfx/hal/inc/gfx_driver_interface.h"
#include "gfx/hal/inc/gfx_default_impl.h"
#include "framework/driver/pmp/drv_pmp_static.h"
#include "system_definitions.h" 

#define MAX_LAYER_COUNT  1
#define MAX_BUFFER_COUNT 1
#define GFX_CONFIG_COLOR_DEPTH  16  //16bit color
#define abs(a)  (((a)>0) ? (a) : -(a))

static uint32_t supportedColorModes = GFX_COLOR_MASK_RGB_565;
const char* DRIVER_NAME = "Generic";
short DISP_HOR_RESOLUTION = 320;
short DISP_VER_RESOLUTION = 240;


//PMP write function
void ys_DeviceWrite(short data)
{
	PMDIN = data;   //PMP data set
	while(PMMODEbits.BUSY);     //wait until(BUSY bit = 1)
	//PMPWaitBusy();
}

//Data send function
void ys_WriteData(short data)
{
    DisplaySetData();           //RS = 1;
    ys_DeviceWrite(data);
}

//Command send function
void ys_WriteCommand(unsigned char cmd)
{
    DisplayEnable();            //CS = 0;
    DisplaySetCommand();        //RS = 0;
    ys_DeviceWrite(cmd) ;
    DisplayDisable();           //CS = 1;
}

//Area set function
void ys_SetArea(short start_x, short start_y, short end_x, short end_y)
{
    ys_WriteCommand(SSD1963_CMD_SET_COLUMN);
    DisplayEnable();
    ys_WriteData(start_x >> 8);
    ys_WriteData(start_x);
    ys_WriteData(end_x >> 8);
    ys_WriteData(end_x);
    DisplayDisable();
    ys_WriteCommand(SSD1963_CMD_SET_PAGE);
    DisplayEnable();
    ys_WriteData(start_y >> 8);
    ys_WriteData(start_y);
    ys_WriteData(end_y >> 8);
    ys_WriteData(end_y);
    DisplayDisable();

}



//-----------------------------------------------------------------------------
// Initialize function
static GFX_Result initialize(GFX_Context* context)
{

    DisplayCmdDataConfig();             // enable RS line
    DisplayDisable();                   // not selected by default
    DisplayConfig();                    // enable chip select line


#if (DISP_HOR_RESOLUTION == 800 || DISP_HOR_RESOLUTION == 640)
    //Flip the hdir
    ys_WriteCommand(SSD1963_CMD_SET_ADDR_MODE);
    DisplayEnable();
    ys_WriteData(0x02);
    DisplayDisable();
#endif

#if (GFX_CONFIG_COLOR_DEPTH == 16)
    //Set 16-bit 5-6-5
    ys_WriteCommand(SSD1963_CMD_SET_DATA_INTERFACE);
    DisplayEnable();
    ys_WriteData(0x03);
    DisplayDisable();
#elif (GFX_CONFIG_COLOR_DEPTH == 24)
    ys_WriteCommand(SSD1963_CMD_SET_DATA_INTERFACE);
    DisplayEnable();
    ys_WriteData(0x02);
    DisplayDisable();
#endif

    ys_WriteCommand(SSD1963_CMD_ON_DISPLAY); // Turn on display; show the image on display

    return GFX_SUCCESS;
}

//------------------------------------------------------------------------------
//Pixel set function
void ys_pixelSet(int x, int y, int color)
{
    static short lastX = 0;
    static short lastY = 0;

    //Don't always set the new position if we don't need to.
    if (!(lastY == y && x == ++lastX)) {
        ys_SetArea(x, y, DISP_HOR_RESOLUTION - 1, DISP_VER_RESOLUTION - 1);
        ys_WriteCommand(SSD1963_CMD_WR_MEMSTART);
        lastX = x;
        lastY = y;
    }

    DisplayEnable();
    DisplaySetData();
    
    ys_DeviceWrite(color);
    DisplayDisable();
}


static GFX_Result pixelSet(const GFX_PixelBuffer* buf,
                           const GFX_Point* pnt,
						   GFX_Color color)
{   
    int x = pnt->x;
    int y = pnt->y;
    
    ys_pixelSet(x, y, color);
 
    return GFX_SUCCESS;
} 


//------------------------------------------------------------------------------
//Line draw function
//Bresenham's line algorithm

void ys_drawLine(int x0, int y0, int x1, int y1, int color)
{   
//    short steep, t;
//    short deltax, deltay, error;
//    short x, y;
//    short ystep;
//
//    
//        y0=DISP_VER_RESOLUTION-y0 -1;                                               
//        y1=DISP_VER_RESOLUTION-y1 -1;
//        steep = (abs(y1 - y0) > abs(x1 - x0));
//        if(steep){
//                t = x0; x0 = y0; y0 = t;
//                t = x1; x1 = y1; y1 = t;
//        }
//        if(x0 > x1) {
//                t = x0; x0 = x1; x1 = t;
//                t = y0; y0 = y1; y1 = t;
//        }
//        deltax = x1 - x0;                                              
//        deltay = abs(y1 - y0);
//        error = 0;
//        y = y0;
//        if(y0 < y1) ystep = 1; else ystep = -1;
//        for(x=x0; x<=x1; x++)
//        {
//                if(steep)  ys_pixelSet(y,x,color); else  ys_pixelSet(x,y,color);
//                error += deltay;
//                if((error << 1) >= deltax) {
//                        y += ystep;
//                        error -= deltax;
//                }
//        }
    
}

GFX_Result drawLine(const GFX_Point* p0,
                    const GFX_Point* p1,
                    const GFX_DrawState* state)
{
    int color = state->color;
    int x0 = p0->x;
    int y0 = p0->y;
    int x1 = p1->x;
    int y1 = p1->y;
    
    ys_drawLine(x0, y0, x1, y1, color);
	
    return(GFX_SUCCESS);
}

//------------------------------------------------------------------------------
//Rectangle fill functin
ys_fillRect(int left, int top, int right, int bottom, int color)
{ 
    int counter = ((bottom + 1) - top)*((right + 1) - left);   //counter shold be 32bit int

    ys_SetArea(left, top, right, bottom);

    ys_WriteCommand(SSD1963_CMD_WR_MEMSTART);
    DisplayEnable();
    DisplaySetData();
    while (counter--)ys_DeviceWrite(color);   //write color data to PMP
    DisplayDisable();
}


GFX_Result fillRect(const GFX_Rect* rect,
                    const GFX_DrawState* state)
{
    int color = state->color;
    
    int left = rect->x;
    int top = rect->y;
    int right = left + rect->width;
    int bottom = top + rect->height;
 
    
    ys_fillRect(left, top, right, bottom, color);
    
    return(GFX_SUCCESS);
}


//Circle draw functin
void ys_drawCircle(int x0, int y0, int r, int color)    //(x_center, y_center, radius, color)
{
        int x = r;
        int y = 0;
        int F = -2 * r + 3;
        
        while(x >= y){
                ys_pixelSet(x0+x, y0+y, color);
                ys_pixelSet(x0-x, y0+y, color);          
                ys_pixelSet(x0+x, y0-y, color);
                ys_pixelSet(x0-x, y0-y, color);          
                ys_pixelSet(x0+y, y0+x, color);
                ys_pixelSet(x0-y, y0+x, color);          
                ys_pixelSet(x0+y, y0-x, color);
                ys_pixelSet(x0-y, y0-x, color);
                if(F >= 0){
                        x--;
                        F -= 4 * x;
                }
                y++;
                F += 4 * y + 2;
        }
}


static void destroy(GFX_Context* context)
{	
	// driver specific shutdown tasks
	if(context->driver_data != GFX_NULL)
	{
		context->memory.free(context->driver_data);
		context->driver_data = GFX_NULL;
	}
	
	// general default shutdown
	defDestroy(context);
}


static GFX_Result layerActiveSet(uint32_t idx)
{
    return GFX_UNSUPPORTED;
}

static GFX_Result layerEnabledSet(GFX_Bool val)
{
    return GFX_UNSUPPORTED;
}

static GFX_Result layerPositionSet(int32_t x, int32_t y)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerSizeSet(int32_t width, int32_t height)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferCountSet(uint32_t count)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferAddressSet(uint32_t idx, GFX_Buffer address)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferCoherentSet(uint32_t idx, GFX_Bool coherent)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferAllocate(uint32_t idx)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerBufferFree(uint32_t idx)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerVisibleSet(GFX_Bool val)
{
	return GFX_UNSUPPORTED;
}

static GFX_Result layerAlphaEnableSet(GFX_Bool enable)
{
	return GFX_UNSUPPORTED;
}

static GFX_Color pixelGet(const GFX_PixelBuffer* buf,
                          const GFX_Point* pnt)
{
	return 0;
}

// function that returns the information for this driver
GFX_Result driverGenericInfoGet(GFX_DriverInfo* info)
{
	if(info == GFX_NULL)
        return GFX_FAILURE;

	// populate info struct
    strcpy(info->name, DRIVER_NAME);
    info->color_formats = supportedColorModes;
    info->layer_count = MAX_LAYER_COUNT;
    
    return GFX_SUCCESS;
}

// function that initialized the driver context
GFX_Result driverGenericContextInitialize(GFX_Context* context)
{  
	// set driver-specific function implementations
	context->hal.initialize = &initialize; 
    context->hal.destroy = &destroy;
//	context->hal.brightnessRangeGet = &brightnessRangeGet;
//	context->hal.brightnessSet = &brightnessSet;
//	context->hal.layerVsyncSet = &vsyncSet;
//	context->hal.vsyncCallbackSet = &vsyncCallbackSet;
//	context->hal.hsyncCallbackSet = &hsyncCallbackSet;
	context->hal.layerActiveSet = &layerActiveSet;
	context->hal.layerEnabledSet = &layerEnabledSet;
	context->hal.layerPositionSet = &layerPositionSet;
	context->hal.layerSizeSet = &layerSizeSet;
	context->hal.layerBufferCountSet = &layerBufferCountSet;
	context->hal.layerBufferAddressSet = &layerBufferAddressSet;
	context->hal.layerBufferCoherentSet = &layerBufferCoherentSet;
	context->hal.layerBufferAllocate = &layerBufferAllocate;
	context->hal.layerBufferFree = &layerBufferFree;
	context->hal.layerVisibleSet = &layerVisibleSet;
	context->hal.layerAlphaEnableSet = &layerAlphaEnableSet;
	
	context->hal.drawPipeline[GFX_PIPELINE_GCU].pixelSet = &pixelSet;
    context->hal.drawPipeline[GFX_PIPELINE_GCU].pixelGet = &pixelGet;
	
	context->hal.drawPipeline[GFX_PIPELINE_GCUGPU].pixelSet = &pixelSet;
    context->hal.drawPipeline[GFX_PIPELINE_GCUGPU].pixelGet = &pixelGet;

    
	//Mchip commentout
	// accelerated draw line disabled for now, something keeps cutting off pixels 
	// towards the bottom of the screen in some cases
	context->hal.drawPipeline[GFX_PIPELINE_GCU].drawLine[GFX_DRAW_LINE][GFX_ANTIALIAS_OFF] = &drawLine;
    
    context->hal.drawPipeline[GFX_PIPELINE_GCU].drawRect[GFX_DRAW_FILL][GFX_ANTIALIAS_OFF] = &fillRect;
	context->hal.drawPipeline[GFX_PIPELINE_GCUGPU].drawRect[GFX_DRAW_FILL][GFX_ANTIALIAS_OFF] = &fillRect;
	
	return GFX_SUCCESS;
}